home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / PTMID3.ZIP / PTMID.C < prev    next >
C/C++ Source or Header  |  1994-07-18  |  15KB  |  526 lines

  1. /*
  2.  * ptmid.c: Creates Protracker MODule files from MIDI files.
  3.  * (My first attempt at Hungarian Notation.. wince!)
  4.  *
  5.  * Author: Andrew Scott  (c)opyright 1994
  6.  *
  7.  * Date: 17/11/1993 ver 0.0
  8.  *       8/1/1994   ver 0.1 - first release
  9.  *       11/2/1994  ver 0.2 - added stats + fixed 15 handles limit
  10.  *       29/6/1994  ver 0.3 - second release
  11.  */
  12.  
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include <string.h>
  16. #include <ctype.h>
  17. #include <io.h>
  18. #include <stdarg.h>
  19. #include <math.h>
  20. #include "ptmid.h"
  21. #include "samples.h"
  22.  
  23. #ifndef R_OK
  24. #define R_OK 4
  25. #endif
  26.  
  27. #if __MSDOS__
  28. #define SEPARATOR '\\'
  29. #else
  30. #define SEPARATOR '/'
  31. #endif
  32.  
  33. /** Define HACKED_VERSION only if compiled under my Turbo C 2.0 variant **/
  34. #define HACKED_VERSION
  35.  
  36. #define WTSP " \t\n"
  37.  
  38. char bDrumch = 9, szId[5];
  39. int fNocopy = 0, fQuiet = 0, fExtend = 0, fStats = 0;
  40. int wRgmode = 0, wPatmax = 128, wMaxchan = 4, wQuantval = 16, wModfmt = 1;
  41. SI *rgpsiDrum[128], **rgppsiIns[129], *psiTree = NULL;
  42. Sz szTitle = "Converted by PTMID!", szQuant = NULL, szProgram;
  43. Fn fnSampath = {0};
  44.  
  45. /*
  46.  * Init: Does all those initialization things (which aren't too involved).
  47.  */
  48. static void Init(void)
  49. {
  50.     int i;
  51.  
  52. #ifdef HACKED_VERSION
  53.   void increase_handles(void);
  54.  
  55.     increase_handles(); /** Increase possible file handles to 40 **/
  56. #endif
  57.  
  58.     rgppsiIns[128] = NULL; /** Make sure sample-info arrays are clear **/
  59.     for (i = 128; i--; ) {
  60.         rgpsiDrum[i] = NULL;
  61.         rgppsiIns[i] = NULL;
  62.     }
  63. }
  64.  
  65. void Error(Sz szMsg, ...)
  66. {
  67.     va_list args;
  68.  
  69.     fprintf(stderr, "%s: ", szProgram);
  70.     va_start(args, szMsg);
  71.     vfprintf(stderr, szMsg, args);
  72.     va_end(args);
  73.     fprintf(stderr, "\n");
  74. }
  75.  
  76. /*
  77.  * BuildFn: Builds a full filename given a string which is the old filename,
  78.  * and a default extension to use if one is not present in the string. After
  79.  * building the new filename, any extension in the old name is removed.
  80.  */
  81. void BuildFn(Fn fnNew, Sz fnOld, Sz fnExt)
  82. {
  83.     Sz fnT = fnNew;
  84.     int fExt = 0;
  85.  
  86.     while (*fnOld) {
  87.         if ('.' == (*(fnT++) = *fnOld)) { /** Copy a char, test for extension **/
  88.             fExt = 1;
  89.             *fnOld = 0; /** yes.. note extension exists and remove it **/
  90.         }
  91.         fnOld++;
  92.     }
  93.     if (!fExt) { /** If no extension found **/
  94.         *(fnT++) = '.';
  95.         while ((*(fnT++) = *(fnExt++))); /** copy the default one - fnExt **/
  96.     } else
  97.         *fnT = 0;
  98. }
  99.  
  100. /*
  101.  * SzReadPfile: Reads the next string from the textfile given and returns it.
  102.  * If file is at end, returns NULL.
  103.  */
  104. Sz SzReadPfile(FILE *pfileText)
  105. {
  106.     int ch, cch = 0, cchMac = 80;
  107.     Sz szT, szStart;
  108.  
  109.     if (feof(pfileText))
  110.         return NULL;
  111.     szStart = szT = (Sz) malloc(80); /** Set aside 80 characters for line **/
  112.     while ((ch = getc(pfileText)) != EOF && ch != '\n') {
  113.         *szT = ch;
  114.         if (++cch == cchMac) { /** If that's not enough **/
  115.             cchMac += 40;
  116.             szStart = (Sz) realloc(szStart, cchMac); /** increase in steps of 40 **/
  117.             szT = szStart + cch;
  118.         } else
  119.             szT++;
  120.     }
  121.     *szT = 0;
  122.     return (Sz) realloc(szStart, cch + 1);
  123. }
  124.  
  125. /*
  126.  * MakePsi: Given a filename and a place to store the new sample info,
  127.  * will set up the defaults for it.
  128.  *
  129.  * date: 30/6/1994
  130.  */
  131. void MakePsi(SI *psi, Sz fnSample)
  132. {
  133.   psi->fnSample = strdup(fnSample);
  134.   psi->pitch = -1;
  135.   psi->perpitch = MIDDLEC;
  136.   psi->freq = FreqGetFn(fnSample);
  137.   psi->sample = -1;
  138.   psi->bFinetune = 0;
  139.   psi->psiL = psi->psiR = NULL;
  140. }
  141.  
  142. /*
  143.  * PsiAddsample: Given a sample's filename, will look it up in the tree
  144.  * and return a pointer to it if it exists, else will create it and return
  145.  * a pointer to the newly created entry.
  146.  */
  147. SI *PsiAddsample(Sz fnSample)
  148. {
  149.     SI *psiT;
  150.  
  151.     if (NULL == psiTree) { /** If nothing in tree **/
  152.         psiT = psiTree = (SI *) malloc(sizeof(SI)); /** create root node **/
  153.     MakePsi(psiT, fnSample);
  154.     } else { /** Else **/
  155.         SI *psiOld;
  156.         int cmp;
  157.  
  158.         psiT = psiTree;
  159.         while (psiT != NULL) { /** find spot for sample in tree **/
  160.             psiOld = psiT;
  161.             cmp = strcmp(psiT->fnSample, fnSample);
  162.             if (!cmp)
  163.                 break;
  164.             else if (0 > cmp)
  165.                 psiT = psiT->psiL;
  166.             else
  167.                 psiT = psiT->psiR;
  168.         }
  169.         if (NULL == psiT) {
  170.             psiT = (SI *) malloc(sizeof(SI)); /** and create entry **/
  171.             if (0 > cmp)
  172.                 psiOld->psiL = psiT;
  173.             else
  174.                 psiOld->psiR = psiT;
  175.       MakePsi(psiT, fnSample);
  176.         }
  177.     }
  178.     return psiT;
  179. }
  180.  
  181. /*
  182.  * PsiPrunePsi: Returns the given sample tree, but without any redundant
  183.  * samples. Any redundant samples are freed. If no samples remain, NULL
  184.  * is returned.
  185.  */
  186. SI *PsiPrunePsi(SI *psi)
  187. {
  188.     if (NULL == psi)
  189.         return NULL;
  190.     psi->psiL = PsiPrunePsi(psi->psiL); /** Prune left and right branches **/
  191.     psi->psiR = PsiPrunePsi(psi->psiR);
  192.     if (-1 == psi->pitch) { /** If root of tree redundant, need to remove it **/
  193.         SI *psiT;
  194.  
  195.         if (NULL == psi->psiL) { /** If no left branch **/
  196.             psiT = psi->psiR;
  197.             free(psi); /** replace root **/
  198.             psi = psiT; /** with right branch **/
  199.         } else if (NULL == psi->psiR) { /** If no right branch **/
  200.             psiT = psi->psiL;
  201.             free(psi); /** replace root **/
  202.             psi = psiT; /** with left branch **/
  203.         } else if (NULL == psi->psiL->psiR) { /** If left branch has no right **/
  204.             psiT = psi->psiL;
  205.             psiT->psiR = psi->psiR; /** put right branch on right of left **/
  206.             free(psi); /** and replace root **/
  207.             psi = psiT; /** with left branch **/
  208.         } else { /** Else.. there's 2 full branches - yuck! **/
  209.             SI *psiOld;
  210.  
  211.             psiT = psi->psiL;
  212.             while (NULL != psiT->psiR) { /** Find rightmost entry on left branch **/
  213.                 psiOld = psiT;
  214.                 psiT = psiT->psiR;
  215.             }
  216.             psiOld->psiR = psiT->psiL;
  217.             psiT->psiL = psi->psiL;
  218.             psiT->psiR = psi->psiR;
  219.             free(psi); /** remove root **/
  220.             psi = psiT; /** and replace it with that entry **/
  221.         }
  222.     }
  223.     return psi;
  224. }
  225.  
  226. /*
  227.  * PitchConv: The supplied token is converted to the equivalent MIDI
  228.  * pitch value, eg. C2 = 60, C#2 = 61. -1 is returned if not a note.
  229.  *
  230.  * date: 30/6/1994
  231.  */
  232. int PitchConv(Sz szTok)
  233. {
  234.   static int rgbPitch[7] = {45, 47, 36, 38, 40, 41, 43};
  235.   int irgb;
  236.  
  237.   if ((irgb = toupper(szTok[0]) - 'A') < 0 || 6 < irgb)
  238.     return -1;
  239.   else if ('#' == szTok[1])
  240.     szTok++;
  241.   return rgbPitch[irgb] + 12 * atoi(szTok + 1) +
  242.     ('#' == szTok[0] ? 1 : 0);
  243. }
  244.  
  245. /*
  246.  * ReadconfigFn: Given the filename of the configuration file, it interprets
  247.  * each line and sets up options and sample-tables.
  248.  *
  249.  * date: 30/6/1994 - added ditto, sample paths, sample frequencies
  250.  *       1/7/1994 - added finetuning for those odd frequencies
  251.  */
  252. void ReadconfigFn(Sz fnConfig)
  253. {
  254.     FILE *pfileConfig;
  255.     Sz szLine, szTok;
  256.   int csz = 0, fError = 0, irgppsiLast = 0;
  257.   static Sz rgszIds[] = {
  258.     "M.K.",
  259.     "TDZ1", "TDZ2", "TDZ3", "M!K!", "5CHN", "6CHN", "7CHN", "8CHN",
  260.     "9CHN", "10CH", "11CH", "12CH", "13CH", "14CH", "15CH", "16CH",
  261.     "17CH", "18CH", "19CH", "20CH", "21CH", "22CH", "23CH", "24CH",
  262.     "25CH", "26CH", "27CH", "28CH", "29CH", "30CH", "31CH", "32CH"
  263.   };
  264.  
  265.     if (NULL == (pfileConfig = fopen(fnConfig, "rt"))) {
  266.         Error("Cannot find config file: %s", fnConfig);
  267.         exit(1);
  268.     }
  269.     while ((szLine = SzReadPfile(pfileConfig)) != NULL) { /** With every line.. **/
  270.         csz++;
  271.     if ('#' != szLine[0] && NULL != (szTok = strtok(szLine, WTSP)))
  272.             if ('0' <= szTok[0] && '9' >= szTok[0] || !strcmp(szTok, "def")) {
  273.                 int irgppsi, cpsi; /** If an instrument definition **/
  274.                 SI **ppsi;
  275.  
  276.                 if ('d' == szTok[0])
  277.                     irgppsi = 128;
  278.                 else
  279.                     irgppsi = atoi(szTok); /** decode instrument **/
  280.         if (irgppsi < 129) {
  281.           szTok = strtok(NULL, WTSP);
  282.           if (!strcmp(szTok, "\"")) /** Check for ditto **/
  283.             if (NULL == rgppsiIns[irgppsi])
  284.               rgppsiIns[irgppsi] = rgppsiIns[irgppsiLast]; /** Yes.. copy **/
  285.             else
  286.               fError = 1;
  287.           else { /** No.. **/
  288.             irgppsiLast = irgppsi;
  289.             while (NULL != szTok) { /*** With every sample.. ***/
  290.               if (NULL == rgppsiIns[irgppsi]) /*** Ensure allocated ***/
  291.                 rgppsiIns[irgppsi] = ppsi = (SI **) malloc(sizeof(SI *) * 2);
  292.               else {
  293.                 ppsi = rgppsiIns[irgppsi];
  294.                 for (cpsi = 2; NULL != *ppsi; cpsi++, ppsi++);
  295.                 rgppsiIns[irgppsi] = ppsi = (SI **) realloc(rgppsiIns[irgppsi],
  296.                   sizeof(SI *) * cpsi);
  297.                 ppsi += cpsi - 2;
  298.               }
  299.               ppsi[0] = PsiAddsample(szTok); /*** Put sample in array ***/
  300.               ppsi[1] = NULL;
  301.               szTok = strtok(NULL, WTSP);
  302.             }
  303.           }
  304.         } else
  305.                     fError = 1;
  306.  
  307.             } else if ('d' == szTok[0] && '0' <= szTok[1] && '9' >= szTok[1]) {
  308.         int irgpsi, tPitch; /** If a percussion definition **/
  309.  
  310.                 if ((irgpsi = atoi(szTok + 1)) < 128 && /** decode instrument **/
  311.          (szTok = strtok(NULL, WTSP)) != NULL) {
  312.                     if (NULL != rgpsiDrum[irgpsi])
  313.                         free(rgpsiDrum[irgpsi]); /** and free up if previously used **/
  314.                     rgpsiDrum[irgpsi] = PsiAddsample(szTok); /** Put sample in array **/
  315.           if ((szTok = strtok(NULL, WTSP)) != NULL &&
  316.            (tPitch = PitchConv(szTok)) != -1) /** If play pitch there **/
  317.             rgpsiDrum[irgpsi]->perpitch = tPitch; /** store it **/
  318.                 } else
  319.                     fError = 1;
  320.  
  321.       } else if (!strcmp(szTok, "spath")) { /** If sample path **/
  322.         if ((szTok = strtok(NULL, WTSP)) == NULL)
  323.           fError = 1;
  324.         else
  325.           strncpy(fnSampath, szTok, MAXFILELEN); /** store it **/
  326.  
  327.       } else if (!strcmp(szTok, "sample")) { /** If sample info **/
  328.                 Sz fnSample;
  329.                 SI *psi;
  330.         int tPitch;
  331.  
  332.         if ((fnSample = strtok(NULL, WTSP)) == NULL) /** Get name **/
  333.                     fError = 1;
  334.         else if ((szTok = strtok(NULL, WTSP)) == NULL)
  335.                     fError = 1;
  336.         else if ((tPitch = PitchConv(szTok)) == -1) /** Get pitch **/
  337.                     fError = 1;
  338.                 else {
  339.           double scale;
  340.  
  341.                     psi = PsiAddsample(fnSample); /** Make sure sample allocated **/
  342.           if (C2FREQUENCY == psi->freq)
  343.             scale = 0;
  344.           else /** Calculate any scaling for special samples **/
  345.             scale = -12 * log(psi->freq / 8287.1369) / log(2.0);
  346.           psi->pitch = tPitch + (int) scale;
  347.           psi->bFinetune = (int) (8 * modf(scale, NULL));
  348.           if ((szTok = strtok(NULL, WTSP)) == NULL)
  349.                         psi->wLppos = 0;
  350.                     else
  351.                         psi->wLppos = atoi(szTok); /** Get loop start **/
  352.           if ((szTok = strtok(NULL, WTSP)) == NULL)
  353.                         psi->wLplen = 0;
  354.                     else
  355.                         psi->wLplen = atoi(szTok); /** Get loop length **/
  356.                 }
  357.  
  358.       } else if (!strcmp(szTok, "modfmt")) /** If module-id **/
  359.         if ((szTok = strtok(NULL, WTSP)) == NULL)
  360.                     fError = 1;
  361.         else {
  362.           int wT;
  363.  
  364.           if ((wT = atoi(szTok)) == 1 || 2 == wT)
  365.             wModfmt = wT;
  366.           else
  367.             fError = 1;
  368.         }
  369.  
  370.       else if (!strcmp(szTok, "maxchan")) /** If max. channels **/
  371.         if ((szTok = strtok(NULL, WTSP)) == NULL)
  372.                     fError = 1;
  373.                 else {
  374.                     int wT;
  375.  
  376.           if ((wT = atoi(szTok)) > 0 && MAXCHANS >= wT)
  377.             wMaxchan = wT; /** store them **/
  378.                     else
  379.                         fError = 1;
  380.                 }
  381.  
  382.             else if (!strcmp(szTok, "drumch")) /** If MIDI percussion channel **/
  383.         if ((szTok = strtok(NULL, WTSP)) == NULL)
  384.                     fError = 1;
  385.                 else {
  386.                     int bT;
  387.  
  388.                     if ((bT = atoi(szTok)) != 0)
  389.                         bDrumch = bT - 1; /** store it **/
  390.                 }
  391.  
  392.       else if (!strcmp(szTok, "rgmode")) /** If range-mode option **/
  393.         if ((szTok = strtok(NULL, WTSP)) == NULL)
  394.           fError = 1;
  395.         else {
  396.           int wT;
  397.  
  398.           if ((wT = atoi(szTok)) >= 0 || 2 >= wT)
  399.             wRgmode = wT; /** store it **/
  400.         }
  401.  
  402.             else if (!strcmp(szTok, "fract") && NULL == szQuant) { /** If quantize frac. **/
  403.                 int wT;
  404.  
  405.         if ((szQuant = strtok(NULL, WTSP)) == NULL ||
  406.                  !(wT = ValidquantSz(szQuant))) /** decode **/
  407.                     fError = 1;
  408.                 else
  409.                     wQuantval = wT; /** and store it **/
  410.  
  411.             } else if (!strcmp(szTok, "extend")) /** If extend-notes flag **/
  412.                 fExtend = 1; /** toggle **/
  413.             else if (!strcmp(szTok, "nocopy")) /** If no-copyright flag **/
  414.                 fNocopy = 1; /** toggle **/
  415.             else
  416.                 fError = 1;
  417.  
  418.             if (fError) { /** If an error at any point, reveal line **/
  419.                 Error("Error in config file: line %d", csz);
  420.                 exit(1); /** and quit **/
  421.             }
  422.         free(szLine);
  423.     }
  424.  
  425.     if (NULL == rgppsiIns[128]) {
  426.         Error("No default instrument defined in config file");
  427.         exit(1);
  428.     }
  429.     if ((psiTree = PsiPrunePsi(psiTree)) == NULL) {
  430.         Error("No sample definitions found in config file");
  431.         exit(1);
  432.     }
  433.   if (1 == wModfmt) /** If Protracker format used **/
  434.     strncpy(szId, rgszIds[wMaxchan], 5); /** store special Id **/
  435. }
  436.  
  437. /*
  438.  * main: Parses arguments to program and opens appropriate MOD and MID files.
  439.  */
  440. int main(int argc, char **argv)
  441. {
  442.     int cNames = 0;
  443.     Sz fnDef, fnConfig = DEFCONFIG;
  444.     Fn fnIn, fnOut;
  445.     FILE *pfileMod;
  446.     Tune *ptuneMusic;
  447.  
  448.     Init();
  449.     if ((szProgram = strrchr(*argv, SEPARATOR)) != NULL)
  450.         szProgram++;
  451.     else
  452.         szProgram = *argv;
  453.     for (argv++; NULL != *argv; argv++) /** Run through all parameters **/
  454.         if ('-' == **argv) /** If parameter is a switch **/
  455.             switch ((*argv)[1]) { /** check what sort **/
  456.                 case 'c':
  457.                     if ((*argv)[2])
  458.                         fnConfig = *argv + 2; /** c: set config file **/
  459.                     break;
  460.                 case 'd':
  461.                     bDrumch = atoi(*argv + 2) - 1; /** d: set drum channel **/
  462.                     break;
  463.                 case 'f': {
  464.                     int wT; /** f: set quantize fraction **/
  465.  
  466.                     if ((wT = ValidquantSz(*argv + 2)))
  467.                         wQuantval = wT;
  468.                     else
  469.                         Error("Invalid quantize fraction - using default");
  470.                     break;
  471.                 }
  472.                 case 'q':
  473.                     fQuiet = !fQuiet; /** q: toggle quiet mode **/
  474.                     break;
  475.                 case 's':
  476.                     fStats = !fStats; /** s: toggle statistics mode **/
  477.                     break;
  478.             }
  479.         else { /** Else must be a filename **/
  480.             if (0 == cNames)
  481.                 BuildFn(fnIn, fnDef = *argv, "mid");
  482.             else if (1 == cNames)
  483.         fnDef = *argv;
  484.             cNames++;
  485.         }
  486.  
  487.     if (1 > cNames || 2 < cNames) { /** If no filenames - error **/
  488.         printf("Use: ptmid [-cFile] [-dChannel] [-fFrac] [-q] [-s] infile[.mid] [outfile[.mod]]\n");
  489.         printf("  version " PTVER "\n");
  490.     printf("  Creates Protracker MOD and MTM files from General MIDI files\n");
  491.         exit(1);
  492.   }
  493.  
  494.     ReadconfigFn(fnConfig);
  495.   /** Ensure output filename has correct extension if not supplied **/
  496.   BuildFn(fnOut, fnDef, (1 == wModfmt) ? "mod" : "mtm");
  497.   if (!fQuiet)
  498.     printf("ptmid ver " PTVER ": Converting '%s' to '%s'\n", fnIn, fnOut);
  499.  
  500.   if (access(fnIn, R_OK)) {
  501.         ERROR;
  502.         exit(1);
  503.     }
  504.     if ((ptuneMusic = PtuneLoadFn(fnIn)) == NULL) {
  505.         Error("Not a legal MIDI file: %s", fnIn);
  506.     exit(1);
  507.     }
  508.  
  509.     if (!fQuiet)
  510.         printf("Analyzing..\n");
  511.     ResolvePtune(ptuneMusic);
  512.  
  513.     if (!fQuiet)
  514.         printf("Writing..\n");
  515.     if ((pfileMod = fopen(fnOut, "wb")) == NULL) {
  516.         ERROR;
  517.     exit(1);
  518.     }
  519.     SavePtunePfile(ptuneMusic, pfileMod);
  520.     fclose(pfileMod);
  521.  
  522.     if (!fQuiet)
  523.         printf("Done.\n");
  524.   return 0;
  525. }
  526.